/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
/* Copyright (C) 2012 Zongyou Yao. All rights reserved.     */

#ifndef AP_INCLUDED
#define AP_INCLUDED

#include <stdarg.h>
#include "fmt.h"

/**
 * It is a checked runtime error to pass a null ap_t to any ap function.
 */
typedef struct ap_t * ap_t;

/**
 * Allocates and returns a new ap_t initialized to n.
 * Exception mem_failed can be raised.
 */
ap_t ap_new(long int n);

/**
 * Interprets str as an integer in base and returns the resulting ap_t.
 * Ignores leading white space and accepts an optional sign followed by
 * one or more digits in base. For 10 < base <= 36, lowercase or uppercase
 * letters are interpreted as digits greater than 9. If end != null, *end
 * points to the character in str that terminated the scan. If str does
 * not specify an integer in base, ap_fromstr returns null and sets *end
 * to str, if end is nonnull. It is a checked runtime error for str == null
 * or for base < 2 or base > 36.
 * Exception mem_failed can be raised.
 */
ap_t ap_fromstr(const char *str, int base, char **end);

/**
 * Returns a long with same sign as x and magnitude |x| mod
 * LONG_MAX + 1.
 */
long int ap_toint(ap_t x);

/**
 * Fills str[0 .. size - 1] with the character representation of x in base
 * and returns str. If str == null, ap_tostr allocates it. Uppercase letters
 * are used for digits that exceed 9 when base > 10. It is a checked runtime
 * error for a nonnull str to be too small or for base < 2 or base > 36.
 */
char *ap_tostr(char *str, int size, int base, ap_t x);

/**
 * A fmt conversion function: consumes a ap_t and formats it like printf's
 * %d. It is a checked runtime error for app or flags to be null.
 * Exception mem_failed can be raised.
 */
void ap_fmt(int code, va_list_box *box, int put(int c, void *cl), void *cl,
            unsigned char flags[], int width, int precision);

/**
 * Deallocates and clears *z. It is a checked runtime error for z or *z to be null.
 */
void ap_free(ap_t *z);

/**
 * return -x.
 * Exception mem_failed can be raised.
 */
ap_t ap_neg(ap_t x);

/**
 * Return the sum x + y.
 * Exception mem_failed can be raised.
 */
ap_t ap_add(ap_t x, ap_t y);

/**
 * Return the difference x - y.
 * Exception mem_failed can be raised.
 */
ap_t ap_sub(ap_t x, ap_t y);

/**
 * Return the product x * y.
 * Exception mem_failed can be raised.
 */
ap_t ap_mul(ap_t x, ap_t y);

/**
 * Return the quotient x / y; see arith_div.
 * It is a checked runtime error for y == 0.
 * Exception mem_failed can be raised.
 */
ap_t ap_div(ap_t x, ap_t y);

/**
 * Return x mod y, see arith_mod. It is a checked runtime error for y == 0.
 * Exception mem_failed can be raised.
 */
ap_t ap_mod(ap_t x, ap_t y);

/**
 * Return the value of x to the power of y mod p.
 * If p == null, returns the value of x to the power of y.
 * It is a checked runtime error for y < 0 or for a nonnull p < 2.
 * Exception mem_failed can be raised.
 */
ap_t ap_pow(ap_t x, ap_t y, ap_t p);

/**
 * Return the sum x + y.
 * Exception mem_failed can be raised.
 */
ap_t ap_addi(ap_t x, long int y);

/**
 * Return the difference x - y.
 * Exception mem_failed can be raised.
 */
ap_t ap_subi(ap_t x, long int y);

/**
 * Return the product x * y.
 * Exception mem_failed can be raised.
 */
ap_t ap_muli(ap_t x, long int y);

/**
 * Return the quotient x / y; see arith_div.
 * It is a checked runtime error for y == 0.
 * Exception mem_failed can be raised.
 */
ap_t ap_divi(ap_t x, long int y);

/**
 * Return x mod y, see arith_mod. It is a checked runtime error for y == 0.
 * Exception mem_failed can be raised.
 */
long ap_modi(ap_t x, long int y);

/**
 * Returns x shifted left by s bits; vacated bits are filled with zeros, and
 * the result has the same sign as x. It is a checked runtime error for s < 0.
 * Exception mem_failed can be raised.
 */
ap_t ap_lshift(ap_t x, int s);

/**
 * Returns x shifted right by s bits, vacated bits are filled with zeros, and
 * the result has the same sign as x. It is a checked runtime error for s < 0.
 * Exception mem_failed can be raised.
 */
ap_t ap_rshift(ap_t x, int s);

/**
 * Return an int < 0, = 0, or > 0 if x < y, x == y, or x > y.
 */
int ap_cmp(ap_t x, ap_t y);

/**
 * Return an int < 0, = 0, or > 0 if x < y, x == y, or x > y.
 */
int ap_cmpi(ap_t x, long int y);

#endif
